﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using DigitsRecognizer.NeuralNetwork;

namespace NeuralNetwork
{
    public abstract class Reader<T>
    {
        protected string final;
        public abstract bool ReadParams(ref List<T[]> list, string login);
        protected abstract void ConvertParams(ref List<T[]> list);
        // neural network
        public virtual bool ReadNeuralNetworkParams(ref List<double> e, ref List<int> NumberOfInputs, ref List<List<List<Neuron>>> HiddenLayer, ref List<List<Neuron>> OutputLayer, string login, WriteReadDestination destination)
        {
            string[] txtfiles;
            switch (destination) 
            { 
                case WriteReadDestination.OneFile:
                    txtfiles = Directory.GetFiles(Application.StartupPath, string.Format("NeuralNetworkParams_{0}.txt", login));
                    break;
                
                default:
                    txtfiles = Directory.GetFiles(Application.StartupPath, string.Format("NeuralNetworkParams_{0}_*", login));
                    break;
            }

            for (int i = 0; i < txtfiles.Length; i++ )
            {
                txtfiles[i] = txtfiles[i].Contains("\\") ? txtfiles[i].Substring(txtfiles[i].LastIndexOf("\\") + 1) : txtfiles[i];
                try
                {
                    FileStream fs = new FileStream(txtfiles[i], FileMode.Open, FileAccess.Read);
                    StreamReader sr = new StreamReader(fs);
                    final = sr.ReadToEnd();
                    sr.Close();
                    ConvertNeuralNetworkParams(ref e, ref NumberOfInputs, ref HiddenLayer, ref OutputLayer);
                }
                catch (IOException ex)
                {
                    Console.WriteLine(ex);
                    return false;
                }
            }
            return true;
        }

        protected virtual void ConvertNeuralNetworkParams(ref List<double> e, ref List<int> NumberOfInputs, ref List<List<List<Neuron>>> HiddenLayer, ref List<List<Neuron>> OutputLayer)
        {
            int indexStart, indexEnd;
            string temp;
            string[] tableString;
            double[] tableDouble;
            double tempDouble;

            // IL tmporary
            int numOfInputs;

            // HL temporary
            List<List<Neuron>> hlTmp = new List<List<Neuron>>();
            int numOfHL;
            int[] neuronsInHL;

            // OL temporary
            List<Neuron> olTmp = new List<Neuron>();
            int numOfOutputs;

            final = final.Replace("\n", " ");
            final = final.Replace("\t", " ");
            final = final.Replace("\r", " ");

            while (final.Length > 0)
            {
                indexStart = final.IndexOf("{E=") + 3;
                indexEnd = final.IndexOf("}");
                if (indexEnd == -1)
                {
                    break;
                }
                temp = final.Substring(indexStart, indexEnd - indexStart);
                tempDouble = Convert.ToDouble(temp);
                e.Add(tempDouble);
                final = final.Substring(indexEnd + 1);
                indexStart = final.IndexOf("NumberOfInputs:") + 15;
                indexEnd = final.IndexOf("}");
                temp = final.Substring(indexStart, indexEnd - indexStart);
                final = final.Substring(indexEnd + 1);
                numOfInputs = Convert.ToInt32(temp.Trim());
                NumberOfInputs.Add(numOfInputs);

                // HL
                indexStart = final.IndexOf("NumberOfLayers:") + 15;
                indexEnd = final.IndexOf(",");
                temp = final.Substring(indexStart, indexEnd - indexStart);
                final = final.Substring(indexEnd);
                numOfHL = Convert.ToInt32(temp.Trim());
                indexStart = final.IndexOf("NeuronsInLayers:") + 16;
                indexEnd = final.IndexOf("}");
                temp = final.Substring(indexStart, indexEnd - indexStart);
                final = final.Substring(indexEnd + 1);
                tableString = temp.Split();
                tableString = tableString.Where(s => s != string.Empty).ToArray<string>();
                neuronsInHL = new int[tableString.Length];
                for (int i = 0; i < tableString.Length; i++)
                {
                    neuronsInHL[i] = Convert.ToInt32(tableString[i]);
                }

                indexStart = final.IndexOf("SynapsesWeights:") + 16;
                indexEnd = final.IndexOf("}");
                temp = final.Substring(indexStart, indexEnd - indexStart);
                final = final.Substring(indexEnd + 1);
                tableString = temp.Split();
                tableString = tableString.Where(s => s != string.Empty).ToArray<string>();
                tableDouble = new double[tableString.Length];
                for (int i = 0; i < tableString.Length; i++)
                {
                    tableDouble[i] = Convert.ToDouble(tableString[i]);
                }

                // dodawanie wag dla HL
                int glblIndex = 0;
                for (int i = 0; i < numOfHL; i++)
                {
                    List<Neuron> hlLayer = new List<Neuron>();
                    for (int j = 0; j < neuronsInHL[i]; j++)
                    {
                        Neuron neuron = new Neuron();
                        if (i == 0)
                        {
                            for (int k = 0; k < numOfInputs; k++, glblIndex++)
                            {
                                neuron[j] = new Dendrit(tableDouble[glblIndex]);
                            }
                        }
                        else
                        {
                            for (int k = 0; k < hlTmp[i - 1].Count; k++, glblIndex++)
                            {
                                neuron[j] = new Dendrit(tableDouble[glblIndex]);
                            }
                        }
                        hlLayer.Add(neuron);
                    }
                    hlTmp.Add(hlLayer);
                }
                HiddenLayer.Add(hlTmp);

                // OL
                indexStart = final.IndexOf("NumberOfOutputs:") + 16;
                indexEnd = final.IndexOf("}");
                temp = final.Substring(indexStart, indexEnd - indexStart);
                final = final.Substring(indexEnd + 1);
                numOfOutputs = Convert.ToInt32(temp);
                indexStart = final.IndexOf("Weights:") + 8;
                indexEnd = final.IndexOf("}");
                temp = final.Substring(indexStart, indexEnd - indexStart);
                final = final.Substring(indexEnd + 1);
                tableString = temp.Split();
                tableString = tableString.Where(s => s != string.Empty).ToArray<string>();
                tableDouble = new double[tableString.Length];
                for (int i = 0; i < tableString.Length; i++)
                {
                    tableDouble[i] = Convert.ToDouble(tableString[i]);
                }

                glblIndex = 0;
                for (int i = 0; i < numOfOutputs; i++)
                {
                    Neuron neuron = new Neuron();
                    for (int j = 0; j < hlTmp.Last().Count; j++, glblIndex++)
                    {
                        neuron[i] = new Dendrit(tableDouble[glblIndex]);
                    }
                    olTmp.Add(neuron);
                }
                OutputLayer.Add(olTmp);
                hlTmp = new List<List<Neuron>>();
                olTmp = new List<Neuron>();
            }
        }

        public virtual bool Clear(string login) 
        {
            try
            {
                FileStream fs = new FileStream(string.Format("NeuralNetworkParams_{0}.txt", login), FileMode.Create, FileAccess.ReadWrite);
                StreamReader sr = new StreamReader(fs);
                final = sr.ReadToEnd();
                sr.Close();
                return true;
            }
            catch (IOException ex)
            {
                Console.WriteLine(ex);
                return false;
            }
        }
    }
}
